/*******************************************************************************
 * Copyright (c) 2009 IBM Corporation and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.aseclipse.osgi.classes
{
    import org.aseclipse.core.runtime.registry.classes.BundleModel;
    import org.aseclipse.core.runtime.registry.classes.ExtensionsParser;
    import org.aseclipse.core.runtime.registry.classes.RegistryConstants;
    import org.aseclipse.osgi.loader.BundleConstants;
    import org.aseclipse.osgi.loader.ResourceLoader;
    
    import flash.events.Event;
    
    import mx.core.Application;
    
    /********************
    * Manage bundle installing.
    * Installing a bundle means loading plugin.xml. 
    * Install bundle one by one.
    */ 
    public class BundleInstallService
    {
        private var installingBundleQueue:Array = new Array();
        private var installingQueueBusy:Boolean = false;
        // cache for easy accessing
        private var installingBundleMap:Object = new Object();
        
        private var framework:BundleFrameworkImpl;
        private var extensionParser:ExtensionsParser;
        
        public static const PLUGIN_ROOT:String = "plugin";
        public static const PLUGINS_ROOT:String = "plugins";
        public function BundleInstallService(frame:BundleFrameworkImpl, parser:ExtensionsParser)
        {
            framework = frame;
            extensionParser = parser;
        }
        
        private function containBundle(id:String):Boolean {
            return installingBundleMap[id] != null;
        }
        /************
        * task has key: 
        *     pluginId:
        *     loadType: 1 // load plugin.xml with above pluginId, its root tag is <plugin/>
        *               2 // load all plugin.xml which extends above pluginId, its root tag is <plugins/>
        * 
        */ 
        // read/write lock on installingQueue
        public function install(task:Object):void {
            if(containBundle(task.pluginId)) {
                trace(task.pluginId + " is already in the resolving list");
                return;
            }
            if(installingQueueBusy == false) {
                installingQueueBusy = true;
                installingBundleQueue.push(task);
                var plugin:String = task.pluginId as String;
                installingBundleMap[plugin] = plugin;
                installingQueueBusy = false;    
                run();
            } else {
                Application.application.callLater(install, [task]);
            }
        }
    
        protected function run():void {
            if(installingBundleQueue.length != 0 && installingQueueBusy == false) {
                installingQueueBusy = true;
                // remove one task from queue
                var task:Object = installingBundleQueue.pop();
                installingQueueBusy = false;
                var pluginId:String = task.pluginId as String;
                var type:String = task.loadType as String;
                if(type == BundleConstants.PATTERN_PLUGIN) {
                    trace(pluginId + " is installing now");
                } else {
                    (framework.getBundle(pluginId) as BundleImpl).extendersLoading = true;
                    trace(pluginId + " extenders are installing now");
                }
                // load plugin.xml
                getResourceLoader().loadPlugin(pluginId, type, loadCompleteHandler, 
            		loadFailureHandler);
            }
        }
        
        private function loadCompleteHandler(pluginId:String, xml:XML):void {
            installingBundleMap[pluginId] = null;
            var plugins:XML = <plugins/>;
            if(xml.localName() == PLUGIN_ROOT) {
                plugins.appendChild(xml);
            } else if(xml.localName() == PLUGINS_ROOT) {
                plugins = xml;
            } else {
                throw new Error("Root node is not <plugin> or <plugins> for " + pluginId);
            }
		    for each(var item:XML in plugins.children()) {
		        var bundleData:BundleModel = extensionParser.parseBundle(item, RegistryConstants.PLUGIN);
                var jsBundle:BundleImpl = new BundleImpl(framework, bundleData);
                bundleData.framework = framework;
                bundleData.owner = jsBundle;
    		    framework.addBundle(jsBundle);
            }
            var bundle:BundleImpl = framework.getBundle(pluginId) as BundleImpl;
            if(xml.localName() == PLUGIN_ROOT) { 
                // notify plugin.xml is installed
                bundle.notifyInstalled(); 
            } else { 
                // notify all extender plugin.xml is installed
                bundle.extendersLoading = false;
                bundle.extendersReady = true;
                bundle.notifyExtendersInstalled();
            }
		    Application.application.callLater(run, null); // continue
        }
        
        private function loadFailureHandler(event:Event):void {
            // should put into error list, setup error queue and listener
            trace(event.toString());
        }
        protected function getResourceLoader():ResourceLoader {
            return new ResourceLoader();
        }
    }
}